// 
// MessageListWindow.cs
// 
// Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
//
// MessageTab.cs is part of SpiderIM.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections.Generic;
using Gtk;
using Glade;

using Galaxium.Core;
using Galaxium.Gui.GtkGui;
using Galaxium.Protocol.Gui;

using L=Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library.Messaging;
using Galaxium.Protocol.Xmpp.Library.Utility;
using Galaxium.Protocol.Xmpp.Library.Core;

// TODO: Sorting

namespace Galaxium.Protocol.Xmpp.GtkGui.Messages
{
	public class MessageListWindow
	{
		private enum Columns { Id, StatusIcon, Contact, Subject, IntTime, Time }

		[Widget] private Window   window;
				
		[Widget] private ToolButton   new_button;
		[Widget] private ToolButton   open_button;
		[Widget] private Entry    filter_entry;
		[Widget] private TreeView treeview;

		private L.Client                     _client;
		private Dictionary<int, TreeIter>    _msg_iters;
		private Dictionary<string, TreeIter> _thr_heads;

		public MessageListWindow (L.Client client)
			:this ()
		{
			ThrowUtility.ThrowIfNull ("client", client);

			var gxml = new XML (null, "MessageListWindow.glade", null, null);
			gxml.Autoconnect (this);

			_client = client;
			
			InitView ();

			open_button.Sensitive = false; // TODO: allow loading a saved message
			filter_entry.Sensitive = false; // TODO: filtering messages by subject
			
			new_button.Clicked += (s, e) => MessageWindow.ComposeNew (_client, JabberID.Empty);

			_client.MessageManager.NewMessageEvent += HandleNewMessage;
			_client.MessageManager.MessageStateChangedEvent += HandleMessageStateChange;

			window.DeleteEvent += HandleDeleteEvent;
			
			window.ShowAll ();
		}

		private MessageListWindow ()
		{
			window = null; new_button = null; open_button = null;
			filter_entry = null; treeview = null;
			_msg_iters = new Dictionary<int, TreeIter> ();
			_thr_heads = new Dictionary<string, TreeIter> ();
		}

		private void HandleDeleteEvent (object o, DeleteEventArgs args)
		{
			_client.MessageManager.NewMessageEvent -= HandleNewMessage;
			_client.MessageManager.MessageStateChangedEvent -= HandleMessageStateChange;
			_client = null;
		}

		private void InitView ()
		{
			treeview.Model = new TreeStore (typeof (int),        // ID
			                                typeof (Gdk.Pixbuf), // StatusIcon
			                                typeof (string),     // Contact
			                                typeof (string),     // Subject
			                                typeof (int),        // IntTime
			                                typeof (string));    // Time

			treeview.AppendColumn (String.Empty, new CellRendererPixbuf (),
			                       "pixbuf", Columns.StatusIcon);
			treeview.AppendColumn ("Contact", new CellRendererText (), "markup", Columns.Contact);
			var c = treeview.AppendColumn ("Subject", new CellRendererText (),
			                               "markup", Columns.Subject);
			c.Expand = true;
			treeview.AppendColumn ("Time", new CellRendererText (), "markup", Columns.Time);
			treeview.ExpanderColumn = c;

			treeview.RowActivated += HandleRowActivated;
			
			LoadMessages ();

			(treeview.Model as TreeSortable).SetSortFunc (3, (model, a, b) => {
				int time1 = (int) (model as TreeStore).GetValue (a, (int)Columns.IntTime);
				int time2 = (int) (model as TreeStore).GetValue (b, (int)Columns.IntTime);
				return time1.CompareTo (time2);
			});

			(treeview.Model as TreeSortable).SetSortColumnId (3, SortType.Descending);
			
			treeview.RulesHint = true;
		}

		private void LoadMessages ()
		{
			foreach (var thread in _client.MessageManager.GetThreads ()) {
				bool head = true;
				foreach (var msghead in _client.MessageManager.GetMessagesInThread (thread)) {
					if (head) {
						var iter = (treeview.Model as TreeStore).AppendNode ();
						_thr_heads [msghead.Thread] = iter;
						_msg_iters [msghead.Id] = iter;
						FillRow (iter, msghead, true);
						head = false;
					} else {
						var iter = (treeview.Model as TreeStore).
							AppendNode (_thr_heads [msghead.Thread]);
						_msg_iters [msghead.Id] = iter;
						FillRow (iter, msghead, false);
					}
				}
			}
		}

		private void HandleNewMessage (int id)
		{
			var head = _client.MessageManager.GetMessage (id);
			Application.Invoke ((a, b) => AddMessage (head));
		}
		
		private void HandleMessageStateChange (int id)
		{
			Application.Invoke (delegate {
				var msg = _client.MessageManager.GetMessage (id);
				var pixbuf = IconUtility.GetIcon (msg.IsUnread ? "message-unread" : "message-read");
				treeview.Model.SetValue (_msg_iters [id], (int) Columns.StatusIcon, pixbuf);
			});
		}
	
		private void HandleRowActivated (object o, RowActivatedArgs args)
		{
			TreeIter iter;
			if (treeview.Model.GetIter (out iter, args.Path))
				MessageWindow.Read (_client, (int) treeview.Model.GetValue (iter, (int) Columns.Id));
		}
		
		private void AddMessage (MessageHead head)
		{
			if (_thr_heads.ContainsKey (head.Thread)) {
				int orig_id =
					(int) treeview.Model.GetValue (_thr_heads [head.Thread], (int) Columns.Id);
				var orig_head = _client.MessageManager.GetMessage (orig_id);
				ReplaceHead (head, orig_head);
			} else {
				AddHead (head);
			}
		}

		private void AddHead (MessageHead head)
		{
			var iter = (treeview.Model as TreeStore).AppendNode ();
			_msg_iters [head.Id] = iter;
			_thr_heads [head.Thread] = iter;
			FillRow (iter, head, true);
		}

		private void ReplaceHead (MessageHead new_head, MessageHead old_head)
		{
			var head_iter = _thr_heads [new_head.Thread];

			_msg_iters [new_head.Id] = head_iter;
			FillRow (head_iter, new_head, true);

			var old_head_iter = (treeview.Model as TreeStore).AppendNode (head_iter);
			_msg_iters [old_head.Id] = old_head_iter;
			FillRow (old_head_iter, old_head, false);
		}

		private void FillRow (TreeIter iter, MessageHead head, bool is_top)
		{
			var icon = IconUtility.GetIcon (head.IsUnread ? "message-unread": "message-read");
			if (icon != null)
				treeview.Model.SetValue (iter, (int) Columns.StatusIcon, icon);
			treeview.Model.SetValue (iter, (int) Columns.Contact, head.Contact.Bare ());
			treeview.Model.SetValue (iter, (int) Columns.Subject, head.Subject);
			treeview.Model.SetValue (iter, (int) Columns.IntTime, (int) head.Time.ToBinary ());
			treeview.Model.SetValue (iter, (int) Columns.Time, head.Time.ToShortDateString ()
			                         + ' ' + head.Time.ToLongTimeString ());
			treeview.Model.SetValue (iter, (int) Columns.Id, head.Id);
		}
	}
}
